home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / dte5_1.zip / FINDREP.C < prev    next >
C/C++ Source or Header  |  1991-02-06  |  34KB  |  1,133 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  *
  4.  * This source code is released into the public domain.
  5.  */
  6.  
  7. /*
  8.  * Name:    dte - Doug's Text Editor program - find/replace module
  9.  * Purpose: This file contains the functions relating to finding text
  10.  *           and replacing text.
  11.  *          It also contains the code for moving the cursor to various
  12.  *           other positions in the file.
  13.  * File:    findrep.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  */
  18.  
  19. #ifdef HPXL
  20. #include "commonh"
  21. #include "findreph"
  22. #include "utilsh"
  23. #else
  24. #include "common.h"
  25. #include "findrep.h"
  26. #include "utils.h"
  27. #endif
  28.  
  29. /*
  30.  * prototypes for all functions in this file
  31.  */
  32. int set_flags ARGS((char *flag_str, int *flags, int *count));
  33. int get_flags ARGS((int lines));
  34. int mystrcmp ARGS((char *s1, char *s2));
  35. int mystrcmpi ARGS((char *s1, char *s2));
  36. void on_screen ARGS((windows *window, text_ptr cursor, int last));
  37. void do_replace ARGS((windows *window, text_ptr start));
  38. void do_last ARGS((windows *window));
  39. void find_string ARGS((windows *window));
  40. void replace_string ARGS((windows *window));
  41. void goto_prep ARGS((windows *window));
  42. void goto_complete ARGS((windows *window, text_ptr cursor));
  43. void goto_marker ARGS((windows *window, int n));
  44. void goto_top_file ARGS((windows *window));
  45. void goto_end_file ARGS((windows *window));
  46. text_ptr scan_forward ARGS((text_ptr start, char *opp, char *target));
  47. text_ptr scan_backward ARGS((text_ptr start, char *opp, char *target));
  48. void match_pair ARGS((windows *window, int forward));
  49. void goto_line ARGS((windows *window));
  50.  
  51. /*
  52.  * find and replace flags
  53.  */
  54. #define F_BACKWARD 0x01 /* search backwards through file */
  55. #define F_GLOBAL   0x02 /* search entire file */
  56. #define F_LOCAL    0x04 /* search only marked block */
  57. #define F_AUTO     0x08 /* behave as if user always answered yes */
  58. #define F_IGNORE   0x10 /* ignore case differences */
  59. #define F_WORD     0x20 /* match whole words only */
  60. #define F_MATCH    0x40 /* match the case of the text being replaced */
  61.  
  62. /*
  63.  * Name:    set_flags
  64.  * Purpose: To set up find and replace flags.
  65.  * Date:    October 1, 1989
  66.  * Passed:  flag_str:   the flags chosen by the user
  67.  * Returns: flags:      the equivalent binary flags
  68.  *          count:      the repeat count embedded in the flags
  69.  *          TRUE if flags were OK, FALSE otherwise
  70.  */
  71. int set_flags(flag_str, flags, count)
  72. char *flag_str;
  73. int *flags;
  74. int *count;
  75. {
  76.     char *p;    /* used to scan through flag_str for flags */
  77.  
  78.     /*
  79.      * start with no flags set, then add those chosen
  80.      */
  81.     *flags = 0;
  82.  
  83.     /*
  84.      * assume just a single find / replace required
  85.      */
  86.     *count = 1;
  87.  
  88.     /*
  89.      * scan flag_str to work out which flags were chosen
  90.      */
  91.     for (p=flag_str; *p; ++p) {
  92.         if (isdigit(*p)) {
  93.             /*
  94.              * extract an embedded repeat count
  95.              */
  96.             *count = atoi(p);
  97.             while (isdigit(*++p)) {
  98.                 ;
  99.             }
  100.             --p;
  101.             if (*count <= 0) {
  102.                 error(WARNING, "bad repeat count: %d", *count);
  103.                 *count = 1;
  104.                 return FALSE;
  105.             }
  106.         }
  107.         else if ((*p = toupper(*p)) == 'B') {
  108.             *flags |= F_BACKWARD;
  109.         }
  110.         else if (*p == 'G') {
  111.             *flags |= F_GLOBAL;
  112.             *count = GLOB_COUNT;
  113.         }
  114.         else if (*p == 'L') {
  115.             *flags |= F_LOCAL;
  116.             *count = GLOB_COUNT;
  117.         }
  118.         else if (*p == 'N') {
  119.             *flags |= F_AUTO;
  120.         }
  121.         else if (*p == 'U') {
  122.             *flags |= F_IGNORE;
  123.         }
  124.         else if (*p == 'W') {
  125.             *flags |= F_WORD;
  126.         }
  127.         else if (*p == 'M') {
  128.             *flags |= F_MATCH;
  129.         }
  130.         else {
  131.             error(WARNING, "unknown flag: %c", *p);
  132.             return FALSE;
  133.         }
  134.     }
  135.     return TRUE;
  136. }
  137.  
  138. /*
  139.  * Name:    get_flags
  140.  * Purpose: To input find and replace flags.
  141.  * Date:    October 1, 1989
  142.  * Passed:  lines:  no. of lines up from bottom of screen
  143.  * Returns: [g_status.flags]:        binary flags set
  144.  *          [g_status.flag_str]:     flags as character string
  145.  *          [g_status.search_count]: repeat find count
  146.  *          OK if flags were entered, ERROR if user wanted to abort
  147.  */
  148. int get_flags(lines)
  149. int lines;
  150. {
  151.     char flag_str[MAX_COLS]; /* temporary copy of g_status.flag_str */
  152.     int flags;               /* temporary copy of g_status.flags */
  153.     int count;               /* temporary copy of g_status.count */
  154.  
  155.     /*
  156.      * use the previous flags as the default
  157.      */
  158.     strcpy(flag_str, g_status.flag_str);
  159.  
  160.     /*
  161.      * keep on asking for flags until something acceptable is entered
  162.      */
  163.     for (;;) {
  164.         if (get_name("Options (B,G,L,M,N,n,U,W): ", lines, flag_str) !=
  165.                 OK) {
  166.             return ERROR;
  167.         }
  168.         if (set_flags(flag_str, &flags, &count)) {
  169.             break;
  170.         }
  171.     }
  172.  
  173.     g_status.flags = flags;
  174.     g_status.search_count = count;
  175.     strcpy(g_status.flag_str, flag_str);
  176.     return OK;
  177. }
  178.  
  179. /*
  180.  * Name:    mystrcmp
  181.  * Purpose: To compare two strings up to the length of the first.
  182.  * Date:    October 1, 1989
  183.  * Passed:  s1: first string to compare
  184.  *          s2: second string to compare
  185.  * Returns: 0 if strings match
  186.  *          <0 if s1 < s2
  187.  *          >0 if s1 > s2
  188.  */
  189. int mystrcmp(s1, s2)
  190. char *s1;
  191. char *s2;
  192. {
  193.     for(;;) {
  194.         if (*s1 == '\0') {
  195.             return 0;
  196.         }
  197.         if (*s1 != *s2) {
  198.             return *s1 - *s2;
  199.         }
  200.         ++s1;
  201.         ++s2;
  202.     }
  203. }
  204.  
  205. /*
  206.  * Name:    mystrcmpi
  207.  * Purpose: To compare two strings up to the length of the first, ignoring
  208.  *           case.
  209.  * Date:    October 1, 1989
  210.  * Passed:  s1: first string to compare
  211.  *          s2: second string to compare
  212.  * Returns: 0 if strings match
  213.  *          <0 if s1 < s2
  214.  *          >0 if s1 > s2
  215.  */
  216. int mystrcmpi(s1, s2)
  217. char *s1;
  218. char *s2;
  219. {
  220.     for(;;) {
  221.         if (*s1 == '\0') {
  222.             return 0;
  223.         }
  224.         if (tolower(*s1) != tolower(*s2)) {
  225.             return tolower(*s1) - tolower(*s2);
  226.         }
  227.         ++s1;
  228.         ++s2;
  229.     }
  230. }
  231.  
  232. /*
  233.  * Name:    on_screen
  234.  * Purpose: To move the cursor to a new position, without redrawing the
  235.  *           window unless the new position is off the window.
  236.  * Date:    October 1, 1989
  237.  * Passed:  window: information allowing access to current window etc
  238.  *          cursor: the new target position in the text
  239.  *          last:   the last line considered to be on the screen (this
  240.  *                   only affects the bottom window)
  241.  */
  242. void on_screen(window, cursor, last)
  243. windows *window;
  244. text_ptr cursor;
  245. int last;
  246. {
  247.     text_ptr p;  /* used to scan from current cursor towards new one */
  248.     int line;    /* used to count screen line */
  249.  
  250.     /*
  251.      * if this is the bottom window displayed, then some lines at the
  252.      *  bottom may need to be reserved for messages.
  253.      * Otherwise, the last line available is simply the bottom line used
  254.      *  for the window.
  255.      */
  256.     last = min(g_display.nlines-last-1, window->bottom_line);
  257.  
  258.     line = window->cline;
  259.     if (window->cursor >= cursor) {
  260.         /*
  261.          * new cursor position is above old one
  262.          */
  263.         for (p=window->cursor; ; p--) {
  264.             if (*p == '\n') {
  265.                 --line;
  266.             }
  267.             if (line < window->top_line) {
  268.                 /*
  269.                  * off top of screen, so place in middle of display
  270.                  */
  271.                 window->cline = window->place_line;
  272.  
  273.                 /*
  274.                  * now check that we are not wasting the top part of the
  275.                  *  screen
  276.                  */
  277.                 line = window->cline;
  278.                 for (p=cursor; ; p--) {
  279.                     if (*p == '\n') {
  280.                         --line;
  281.                     }
  282.                     if (*p == '\0') {
  283.                         window->cline -= line - window->top_line;
  284.                         break;
  285.                     }
  286.                     if (line < window->top_line) {
  287.                         break;
  288.                     }
  289.                 }
  290.                 break;
  291.             }
  292.             if (p <= cursor) {
  293.                 /*
  294.                  * position found on screen
  295.                  */
  296.                 window->cline = line;
  297.                 break;
  298.             }
  299.         }
  300.     }
  301.     else {
  302.         /*
  303.          * new cursor position is below current one
  304.          */
  305.         for (p=window->cursor; ; p++) {
  306.             if (*p == '\n') {
  307.                 ++line;
  308.             }
  309.             if (line > last) {
  310.                 /*
  311.                  * off bottom of screen or window
  312.                  */
  313.                 window->cline = window->place_line;
  314.  
  315.                 /*
  316.                  * now check that we are not wasting the bottom part of the
  317.                  *  screen
  318.                  */
  319.                 line = window->cline;
  320.                 for (p=cursor; ; p++) {
  321.                     if (*p == '\n') {
  322.                         ++line;
  323.                     }
  324.                     if (*p == '\0') {
  325.                         window->cline += last - line;
  326.                         break;
  327.                     }
  328.                     if (line > last) {
  329.                         break;
  330.                     }
  331.                 }
  332.                 break;
  333.             }
  334.             if (p >= cursor) {
  335.                 window->cline = line;
  336.                 break;
  337.             }
  338.         }
  339.     }
  340.     window->cursor = cursor;
  341. }
  342.  
  343. /*
  344.  * Name:    do_replace
  345.  * Purpose: To replace text once match has been found.
  346.  * Date:    October 1, 1989
  347.  * Passed:  window: information allowing access to current window etc
  348.  *          start:  location of start of matched text
  349.  */
  350. void do_replace(window, start)
  351. windows *window;
  352. text_ptr start;
  353. {
  354.     int old_len;             /* length of original text */
  355.     int new_len;             /* length of replacement text */
  356.     text_ptr source;         /* source of block move */
  357.     text_ptr dest;           /* destination of block move */
  358.     long number;             /* number of characters moved */
  359.     char new_text[MAX_COLS]; /* replacement text (case matched) */
  360.  
  361.     old_len = strlen(g_status.pattern);
  362.     new_len = strlen(g_status.subst);
  363.  
  364.     /*
  365.      * unless case is to be matched, the replacement text is exactly
  366.      *  what the user entered
  367.      */
  368.     strcpy(new_text, g_status.subst);
  369.  
  370.     if (g_status.flags & F_MATCH) {
  371.         /*
  372.          * change case of new text to match old
  373.          */
  374.         for (dest=new_text, source=start; *dest; ++dest) {
  375.             if (isupper(*source)) {
  376.                 *dest = toupper(*dest);
  377.             }
  378.             else if (islower(*source)) {
  379.                 *dest = tolower(*dest);
  380.             }
  381.  
  382.             /*
  383.              * if the replacement is longer than the original text, then
  384.              *  keep on matching the case of the last character of the
  385.              *  original text
  386.              */
  387.             if (++source >= start + old_len) {
  388.                 --source;
  389.             }
  390.         }
  391.     }
  392.  
  393.     /*
  394.      * move the text to either make room for the extra replacement text
  395.      *  or to close up the gap left
  396.      */
  397.     source = start + old_len;
  398.     dest = start + new_len;
  399.     number = g_status.end_mem - source;
  400.     hw_move(dest, source, number);
  401.  
  402.     /*
  403.      * insert the replacement text
  404.      */
  405.     for (dest=start, source=new_text; *source; ) {
  406.         *dest++ = *source++;
  407.     }
  408.  
  409.     /*
  410.      * fix up any affected marks
  411.      */
  412.     fix_marks(window, start, -(long)old_len);
  413.     fix_marks(window, start, (long) new_len);
  414. }
  415.  
  416. /*
  417.  * Name:    do_last
  418.  * Purpose: To repeat the previous find or replace operation.
  419.  * Date:    October 1, 1989
  420.  * Passed:  window: information allowing access to current window etc
  421.  * Notes:   This function performs a very simple-minded and inefficient
  422.  *           text matching algorithm. When I have more time, I might try
  423.  *           replacing this with something like the Boyer-Moore string
  424.  *           pattern matching algorithm...
  425.  */
  426. void do_last(window)
  427. windows *window;
  428. {
  429.     int len;                /* length of current line */
  430.     int count;              /* number of matches still to be made */
  431.     text_ptr start;         /* start of area to be searched */
  432.     text_ptr end;           /* end of area to be searched */
  433.     text_ptr orig;          /* original cursor location in text */
  434.     text_ptr cursor;        /* where cursor line would be if stopped now */
  435.     text_ptr final_cursor;  /* where cursor should be placed at end */
  436.     char *pattern;          /* pattern to be searched for */
  437.     int pat_len;            /* length of pattern */
  438.     int rep_len;            /* length of replacement text */
  439.     int result;             /* find/replace this one? */
  440.     int backwards;          /* searching backwards? */
  441.     cmp_func cmp;           /* string comparison function in use */
  442.  
  443.     un_copy_line(window);
  444.  
  445.     if (strlen(g_status.pattern) == 0) {
  446.         error(WARNING, "nothing to search for");
  447.         return;
  448.     }
  449.  
  450.     /*
  451.      * save current cursor position as previous
  452.      */
  453.     len = linelen(window->cursor) - 1;
  454.     if (window->ccol < len) {
  455.         len = window->ccol;
  456.     }
  457.     orig = window->cursor + len;
  458.     window->file_info->marker[PREVIOUS] = orig;
  459.  
  460.     /*
  461.      * work out where to start and end searching
  462.      */
  463.     backwards = g_status.flags & F_BACKWARD;
  464.     pattern = g_status.pattern;
  465.     pat_len = strlen(pattern);
  466.     rep_len = strlen(g_status.subst);
  467.     if (g_status.flags & F_GLOBAL) {
  468.         if (backwards) {
  469.             end = window->file_info->start_text;
  470.             start = window->file_info->end_text-1 - pat_len;
  471.             cursor = start - prelinelen(start);
  472.         }
  473.         else {
  474.             cursor = start = window->file_info->start_text;
  475.             end = window->file_info->end_text-1 - pat_len;
  476.         }
  477.     }
  478.     else if (g_status.flags & F_LOCAL) {
  479.         if (!window->file_info->visible ||
  480.                 window->file_info->marker[START_BLOCK] >=
  481.                 window->file_info->marker[END_BLOCK]) {
  482.             error(WARNING, "no block to search");
  483.             return;
  484.         }
  485.         if (backwards) {
  486.             end = window->file_info->marker[START_BLOCK];
  487.             start = window->file_info->marker[END_BLOCK] - pat_len;
  488.             cursor = window->file_info->marker[END_BLOCK] -
  489.                     prelinelen(window->file_info->marker[END_BLOCK]);
  490.         }
  491.         else {
  492.             start = window->file_info->marker[START_BLOCK];
  493.             end = window->file_info->marker[END_BLOCK] - pat_len;
  494.             cursor = window->file_info->marker[START_BLOCK] -
  495.                     prelinelen(window->file_info->marker[START_BLOCK]);
  496.         }
  497.     }
  498.     else {
  499.         if (backwards) {
  500.             end = window->file_info->start_text;
  501.             start = orig - pat_len;
  502.         }
  503.         else {
  504.             start = orig + 1;
  505.             end = window->file_info->end_text-1 - pat_len;
  506.         }
  507.         cursor = window->cursor;
  508.     }
  509.  
  510.     /*
  511.      * work out how to compare
  512.      */
  513.     if (g_status.flags & (F_IGNORE | F_MATCH)) {
  514.         cmp = mystrcmpi;
  515.     }
  516.     else {
  517.         cmp = mystrcmp;
  518.     }
  519.  
  520.     /*
  521.      * find the required number of matches
  522.      */
  523.     count = g_status.search_count;
  524.     while (count > 0) {
  525.         /*
  526.          * check if finished searching
  527.          */
  528.         if (backwards) {
  529.             if (start < end) {
  530.                 break;
  531.             }
  532.         }
  533.         else {
  534.             if (start > end) {
  535.                 break;
  536.             }
  537.         }
  538.  
  539.         /*
  540.          * keep track of where cursor line may start
  541.          */
  542.         if (*start == '\n') {
  543.             cursor = start+1;
  544.         }
  545.  
  546.         /*
  547.          * try for match
  548.          */
  549.         if ((*cmp)(pattern, (char *)start) == 0) {
  550.             if (!(g_status.flags & F_WORD) ||
  551.                     !(myisalnum(*(start-1)) || myisalnum(*(start+pat_len)))
  552.                     ) {
  553.                 /*
  554.                  * we have a valid match
  555.                  */
  556.                 --count;
  557.  
  558.                 /*
  559.                  * position the cursor to show the user
  560.                  */
  561.                 if (backwards) {
  562.                     /*
  563.                      * since we have not got to the \n yet, we must
  564.                      *  scan backwards until we find it, so that we
  565.                      *  can display the screen properly.
  566.                      */
  567.                     cursor = start - prelinelen(start);
  568.                 }
  569.                 window->ccol = (int) (start - cursor);
  570.                 if (window->ccol >= g_display.ncols) {
  571.                     window->ccol = g_display.ncols-1;
  572.                 }
  573.  
  574.                 /*
  575.                  * since this might be the last match, remember the
  576.                  *  spot so that the cursor can be left there at the
  577.                  *  end
  578.                  */
  579.                 final_cursor = cursor;
  580.  
  581.                 if (g_status.flags & F_AUTO) {
  582.                     if (g_status.replace) {
  583.                         /*
  584.                          * replace the string
  585.                          */
  586.                         do_replace(window, start);
  587.                         if (!backwards) {
  588.                             /*
  589.                              * replace may have changed the position of the
  590.                              *  end of the search
  591.                              */
  592.                             end += rep_len - pat_len;
  593.  
  594.                             /*
  595.                              * don't risk recursive replace!
  596.                              */
  597.                             start += rep_len - 1;
  598.                             /*
  599.                              * this fixes a problem with a replacement
  600.                              *  containing the pattern combined with
  601.                              *  multiple ^L replaces...
  602.                              */
  603.                             window->ccol = (int) (start - cursor);
  604.                             if (window->ccol >= g_display.ncols) {
  605.                                 window->ccol = g_display.ncols-1;
  606.                             }
  607.                         }
  608.                     }
  609.                 }
  610.                 else {
  611.                     /*
  612.                      * see if it is on the current screen, and if so
  613.                      *  adjust cursor line accordingly; otherwise
  614.                      *  place in center of screen
  615.                      */
  616.                     on_screen(window, cursor, 1);
  617.  
  618.                     /*
  619.                      * if necessary, ask the user if this replacement
  620.                      *  should be made, or if this is the desired
  621.                      *  occurrence for find
  622.                      */
  623.                     if (g_status.replace || count > 0) {
  624.                         /*
  625.                          * arrange for matched text to be highlighted
  626.                          */
  627.                         g_status.match_start = start;
  628.                         g_status.match_end = start + pat_len;
  629.  
  630.                         /*
  631.                          * find out what to do
  632.                          */
  633.                         set_prompt("this one? (y/n/a/q): ", 1);
  634.                         result = display(get_ynaq, 1);
  635.  
  636.                         /*
  637.                          * remove highlighting
  638.                          */
  639.                         g_status.match_start = g_status.match_end;
  640.  
  641.                         /*
  642.                          * do whatever is required
  643.                          */
  644.                         switch (result) {
  645.                         case A_ABORT:
  646.                         case A_QUIT:
  647.                             return;
  648.                         case A_ALWAYS:
  649.                             /*
  650.                              * switch to automatic mode
  651.                              */
  652.                             g_status.flags |= F_AUTO;
  653.                             if (!g_status.replace) {
  654.                                 break;
  655.                             }
  656.  
  657.                             /*
  658.                              * if replacing, then fall through to
  659.                              *  replace this one before moving on to
  660.                              *  the rest of them
  661.                              */
  662.                         case A_YES:
  663.                             if (g_status.replace) {
  664.                                 /*
  665.                                  * replace the string
  666.                                  */
  667.                                 do_replace(window, start);
  668.                                 if (!backwards) {
  669.                                     /*
  670.                                      * see comments above
  671.                                      */
  672.                                     end += rep_len - pat_len;
  673.                                     start += rep_len - 1;
  674.                                     window->ccol = (int) (start - cursor);
  675.                                     if (window->ccol >= g_display.ncols) {
  676.                                         window->ccol = g_display.ncols-1;
  677.                                     }
  678.                                 }
  679.                             }
  680.                             else {
  681.                                 /*
  682.                                  * found desired occurrence
  683.                                  */
  684.                                 return;
  685.                             }
  686.                             break;
  687.                         case A_NO:
  688.                             /*
  689.                              * keep looking
  690.                              */
  691.                             break;
  692.                         }
  693.                     }
  694.                 }
  695.             }
  696.         }
  697.         /*
  698.          * try again starting one character nearer the end
  699.          */
  700.         if (backwards) {
  701.             --start;
  702.         }
  703.         else {
  704.             ++start;
  705.         }
  706.     }
  707.  
  708.     /*
  709.      * report no matches if necessary
  710.      */
  711.     if (count == g_status.search_count) {
  712.         error(WARNING, "no match");
  713.     }
  714.     else {
  715.         /*
  716.          * leave the cursor on the final match
  717.          */
  718.         on_screen(window, final_cursor, 1);
  719.     }
  720. }
  721.  
  722. /*
  723.  * Name:    find_string
  724.  * Purpose: To set up and perform a find operation.
  725.  * Date:    October 1, 1989
  726.  * Passed:  window: information allowing access to current window etc
  727.  */
  728. void find_string(window)
  729. windows *window;
  730. {
  731.     char pattern[MAX_COLS];  /* text to be found */
  732.  
  733.     /*
  734.      * get replacement text, using previous as default
  735.      */
  736.     strcpy(pattern, g_status.pattern);
  737.     if (get_name("String to find: ", 1, pattern) != OK) {
  738.         return;
  739.     }
  740.     strcpy(g_status.pattern, pattern);
  741.  
  742.     /*
  743.      * get find options to use
  744.      */
  745.     if (get_flags(2) != OK) {
  746.         return;
  747.     }
  748.  
  749.     /*
  750.      * record that this is a find operation
  751.      */
  752.     g_status.replace = FALSE;
  753.  
  754.     /*
  755.      * pretend we are repeating the previous find
  756.      */
  757.     do_last(window);
  758. }
  759.  
  760. /*
  761.  * Name:    replace_string
  762.  * Purpose: To set up and perform a replace operation.
  763.  * Date:    October 1, 1989
  764.  * Passed:  window: information allowing access to current window etc
  765.  */
  766. void replace_string(window)
  767. windows *window;
  768. {
  769.     char pattern[MAX_COLS];  /* the old and replacement text */
  770.  
  771.     /*
  772.      * get the old text, using the previous as the default
  773.      */
  774.     strcpy(pattern, g_status.pattern);
  775.     if (get_name("String to find: ", 1, pattern) != OK) {
  776.         return;
  777.     }
  778.     strcpy(g_status.pattern, pattern);
  779.  
  780.     /*
  781.      * get the replacement text, using the previous as the default
  782.      */
  783.     strcpy(pattern, g_status.subst);
  784.     if (get_name("Replacement:    ", 2, pattern) != OK) {
  785.         return;
  786.     }
  787.     strcpy(g_status.subst, pattern);
  788.  
  789.     /*
  790.      * get the replace flags
  791.      */
  792.     if (get_flags(3) != OK) {
  793.         return;
  794.     }
  795.  
  796.     /*
  797.      * record that this is a replace operation
  798.      */
  799.     g_status.replace = TRUE;
  800.  
  801.     /*
  802.      * go away and do the replace
  803.      */
  804.     do_last(window);
  805. }
  806.  
  807. /*
  808.  * Name:    goto_prep
  809.  * Purpose: To get ready to perform a goto operation, mainly by recording
  810.  *           the current position as previous.
  811.  * Date:    October 1, 1989
  812.  * Passed:  window: information allowing access to current window etc
  813.  */
  814. void goto_prep(window)
  815. windows *window;
  816. {
  817.     int len;  /* length of cursor line */
  818.  
  819.     /*
  820.      * make sure there is no confusion with the line buffer
  821.      */
  822.     un_copy_line(window);
  823.  
  824.     /*
  825.      * set the previous marker to the cursor position
  826.      */
  827.     len = linelen(window->cursor);
  828.     if (window->ccol < len) {
  829.         len = window->ccol;
  830.     }
  831.     window->file_info->marker[PREVIOUS] = window->cursor + len;
  832. }
  833.  
  834. /*
  835.  * Name:    goto_complete
  836.  * Purpose: To clean up after a goto operation, mainly by making sure the
  837.  *           cursor is nicely positioned on the screen.
  838.  * Date:    October 1, 1989
  839.  * Passed:  window: information allowing access to current window etc
  840.  *          cursor: final destination in file buffer
  841.  */
  842. void goto_complete(window, cursor)
  843. windows *window;
  844. text_ptr cursor;
  845. {
  846.     on_screen(window, cursor - prelinelen(cursor), 0);
  847.     window->ccol = prelinelen(cursor);
  848.     if (window->ccol >= g_display.ncols) {
  849.         window->ccol = g_display.ncols-1;
  850.     }
  851. }
  852.  
  853. /*
  854.  * Name:    goto_marker
  855.  * Purpose: To move the cursor to a particular position marker.
  856.  * Date:    October 1, 1989
  857.  * Passed:  window: information allowing access to current window etc
  858.  *          n:      the position marker to be used
  859.  * Notes:   n must be in the range 0 .. 9
  860.  */
  861. void goto_marker(window, n)
  862. windows *window;
  863. int n;
  864. {
  865.     text_ptr cursor;  /* desired cursor position */
  866.  
  867.     un_copy_line(window);
  868.     cursor = window->file_info->marker[n];
  869.     if (cursor) {
  870.         goto_prep(window);
  871.         goto_complete(window, cursor);
  872.     }
  873.     else {
  874.         error(WARNING, "no marker set");
  875.     }
  876. }
  877.  
  878. /*
  879.  * Name:    goto_top_file
  880.  * Purpose: To move the cursor to the top of the file.
  881.  * Date:    October 1, 1989
  882.  * Passed:  window: information allowing access to current window etc
  883.  */
  884. void goto_top_file(window)
  885. windows *window;
  886. {
  887.     goto_prep(window);
  888.     goto_complete(window, window->file_info->start_text);
  889. }
  890.  
  891. /*
  892.  * Name:    goto_end_file
  893.  * Purpose: To move the cursor to the end of the file.
  894.  * Date:    October 1, 1989
  895.  * Passed:  window: information allowing access to current window etc
  896.  */
  897. void goto_end_file(window)
  898. windows *window;
  899. {
  900.     if (window->file_info->end_text > window->file_info->start_text) {
  901.         goto_prep(window);
  902.         goto_complete(window, window->file_info->end_text-1); /* -1 for \0
  903.                                                             at end of text */
  904.     }
  905. }
  906.  
  907. /*
  908.  * Name:    scan_forward
  909.  * Purpose: To find the corresponding occurrence of target, ignoring
  910.  *           embedded pairs of opp and target, searching forwards.
  911.  * Date:    October 1, 1989
  912.  * Passed:  start:  position of character to be paired
  913.  *          opp:    the opposite to target, if any
  914.  *          target: the string to be found
  915.  * Returns: the location of the corresponding target in the text buffer
  916.  */
  917. text_ptr scan_forward(start, opp, target)
  918. text_ptr start;
  919. char *opp;
  920. char *target;
  921. {
  922.     int count = 0;  /* number of unmatched opposites found */
  923.  
  924.     while (*++start) {
  925.         if (opp && mystrcmpi(opp, (char *) start) == 0) {
  926.             count++;
  927.         }
  928.         else if (mystrcmpi(target, (char *) start) == 0) {
  929.             if (count == 0) {
  930.                 break;
  931.             }
  932.             --count;
  933.         }
  934.     }
  935.     return start;
  936. }
  937.  
  938. /*
  939.  * Name:    scan_backward
  940.  * Purpose: To find the corresponding occurrence of target, ignoring
  941.  *           embedded pairs of opp and target, searching backwards.
  942.  * Date:    October 1, 1989
  943.  * Passed:  start:  position of character to be paired
  944.  *          opp:    the opposite to target, if any
  945.  *          target: the string to be found
  946.  * Returns: the location of the corresponding target in the text buffer
  947.  */
  948. text_ptr scan_backward(start, opp, target)
  949. text_ptr start;
  950. char *opp;
  951. char *target;
  952. {
  953.     int count = 0;  /* number of unmatched opposites found */
  954.  
  955.     while (*--start) {
  956.         if (opp && mystrcmpi(opp, (char *) start) == 0) {
  957.             count++;
  958.         }
  959.         else if (mystrcmpi(target, (char *) start) == 0) {
  960.             if (count == 0) {
  961.                 break;
  962.             }
  963.             --count;
  964.         }
  965.     }
  966.     return start;
  967. }
  968.  
  969. /*
  970.  * Name:    match_pair
  971.  * Purpose: To find the corresponding pair to the character under the
  972.  *           cursor.
  973.  * Date:    October 1, 1989
  974.  * Passed:  window:     information allowing access to current window etc
  975.  *          forward:    the user would prefer to move forwards?
  976.  * Notes:   If the cursor character differs from its pair, then the search
  977.  *           direction is chosen automatically, regardless of what the
  978.  *           user requested.
  979.  *          The direction only affects single and double quotes.
  980.  *          Searching is very simple-minded, and does not cope with things
  981.  *           like brackets embedded within quoted strings.
  982.  */
  983. void match_pair(window, forward)
  984. windows *window;
  985. int forward;
  986. {
  987.     text_ptr orig;  /* cursor location in text */
  988.  
  989.     /*
  990.      * make sure the character under the cursor is one that has a
  991.      *  matched pair
  992.      */
  993.     un_copy_line(window);
  994.     if (window->ccol >= linelen(window->cursor)) {
  995.         return;
  996.     }
  997.     orig = window->cursor + window->ccol;
  998.     if (strchr("[]{}()\"\'/\*bBeE", *orig) == NULL) {
  999.         return;
  1000.     }
  1001.     if (*orig == '/' && *(orig+1) != '*') {
  1002.         return;
  1003.     }
  1004.     if (*orig == '*' && *(orig+1) != '/') {
  1005.         return;
  1006.     }
  1007.     if (*orig == 'b' || *orig == 'B') {
  1008.         if (mystrcmpi("begin", (char *) orig) != 0) {
  1009.             return;
  1010.         }
  1011.     }
  1012.     if (*orig == 'e' || *orig == 'E') {
  1013.         if (mystrcmpi("end", (char *) orig) != 0) {
  1014.             return;
  1015.         }
  1016.     }
  1017.  
  1018.     /*
  1019.      * record the cursor position as previous
  1020.      */
  1021.     goto_prep(window);
  1022.  
  1023.     /*
  1024.      * find the matching pair
  1025.      */
  1026.     switch (tolower(*orig)) {
  1027.     case '[':
  1028.         orig = scan_forward(orig, "[", "]");
  1029.         break;
  1030.     case '(':
  1031.         orig = scan_forward(orig, "(", ")");
  1032.         break;
  1033.     case '{':
  1034.         orig = scan_forward(orig, "{", "}");
  1035.         break;
  1036.     case 'b':
  1037.         orig = scan_forward(orig, "begin", "end");
  1038.         break;
  1039.     case ']':
  1040.         orig = scan_backward(orig, "]", "[");
  1041.         break;
  1042.     case ')':
  1043.         orig = scan_backward(orig, ")", "(");
  1044.         break;
  1045.     case '}':
  1046.         orig = scan_backward(orig, "}", "{");
  1047.         break;
  1048.     case 'e':
  1049.         orig = scan_backward(orig, "end", "begin");
  1050.         break;
  1051.     case '"':
  1052.         if (forward) {
  1053.             orig = scan_forward(orig, NULL, "\"");
  1054.         }
  1055.         else {
  1056.             orig = scan_backward(orig, NULL, "\"");
  1057.         }
  1058.         break;
  1059.     case '\'':
  1060.         if (forward) {
  1061.             orig = scan_forward(orig, NULL, "\'");
  1062.         }
  1063.         else {
  1064.             orig = scan_backward(orig, NULL, "\'");
  1065.         }
  1066.         break;
  1067.     case '/':
  1068.         orig = scan_forward(orig, NULL, "*\/");
  1069.         break;
  1070.     case '*':
  1071.         orig = scan_backward(orig, NULL, "/\*");
  1072.         break;
  1073.     }
  1074.  
  1075.     /*
  1076.      * searching backward may leave us on the leading \0
  1077.      */
  1078.     if (orig < window->file_info->start_text) {
  1079.         orig = window->file_info->start_text;
  1080.     }
  1081.  
  1082.     /*
  1083.      * now show the user what we have found
  1084.      */
  1085.     goto_complete(window, orig);
  1086. }
  1087.  
  1088. /*
  1089.  * Name:    goto_line
  1090.  * Purpose: To move the cursor to a particular line in the file
  1091.  * Date:    October 1, 1989
  1092.  * Passed:  window: information allowing access to current window etc
  1093.  * Notes:   Counting lines from the start of the file buffer is not
  1094.  *           very efficient...
  1095.  */
  1096. void goto_line(window)
  1097. windows *window;
  1098. {
  1099.     int number;             /* line number selected */
  1100.     int i;                  /* lines passed so far */
  1101.     char num_str[MAX_COLS]; /* line number as string */
  1102.     text_ptr p;             /* used to scan through file counting lines */
  1103.  
  1104.     /*
  1105.      * find out where we are going
  1106.      */
  1107.     strcpy(num_str, "");
  1108.     if (get_name("Line number: ", 1, num_str) != OK) {
  1109.         return;
  1110.     }
  1111.     number = atoi(num_str);
  1112.  
  1113.     /*
  1114.      * start from the start of the file, and count lines until we
  1115.      *  get there.
  1116.      */
  1117.     un_copy_line(window);
  1118.     p = window->file_info->start_text;
  1119.     for (i=1; i < number; i++) {
  1120.         p = find_next(p);
  1121.         if (p == NULL) {
  1122.             error(WARNING, "only %d lines in file", i);
  1123.             return;
  1124.         }
  1125.     }
  1126.  
  1127.     /*
  1128.      * found the line, now note the previous position and show the user.
  1129.      */
  1130.     goto_prep(window);
  1131.     goto_complete(window, p);
  1132. }
  1133.